//-----------------------------------------------------------------------
// <copyright file="CompileTestSummary.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>James Hartill</author>
// <email></email>
// <date>2004-03-23</date>
// <summary>This task compiles the test results xml file into a summary and produces an xml document.</summary>
//-----------------------------------------------------------------------

namespace Microsoft.Sdc.Tasks
{
    using System;
    using System.IO;
    using System.Xml;
    using System.Xml.Xsl;
    using System.Xml.XPath;
    using System.Globalization;
    using System.Collections;
    using Microsoft.Sdc.Tasks.Configuration;
    using Microsoft.Build.Framework;

    /// <summary>
    /// This task compiles the test results xml file into a summary and produces an xml document.
    /// </summary>
    /// <remarks>
    /// <code><![CDATA[<CompileTestSummary Categories="categories" TestPass="testPass" TestFail="testFail" TestNoRun="testNoRun" TestTotal="testTotal" TestTimeTaken="testTimeTaken" OutputXmlFilename="outputXmlFilename" XsltFilename="xsltFilename" >
    ///                     <Output TaskParameter="TotalTestFailures" ItemName="TotalTestFailuresItem" />
    ///                 </CompileTestSummary>
    /// ]]></code>
    /// <para>where:</para>
    /// <para><i>categories (Required)</i></para>
    /// List of the names of the test categories to be summarised.
    /// <para>
    /// </para>
    /// <para><i>testPass (Required)</i></para>
    /// <para>
    /// List of the number of passed tests for each of the categories specified in <i>categories</i>. This will probably come from running the NUnit test <see cref="Microsoft.Sdc.Tasks.Tools.NUnit"/>.
    /// </para>
    /// <para><i>testFail (Required)</i></para>
    /// <para>
    /// List of the number of failed tests for each of the categories specified in <i>categories</i>. This will probably come from running the NUnit test <see cref="Microsoft.Sdc.Tasks.Tools.NUnit"/>.
    /// </para>
    /// <para><i>testNoRun (Required)</i></para>
    /// <para>
    /// List of the number of tests not run for each of the categories specified in <i>categories</i>. This will probably come from running the NUnit test <see cref="Microsoft.Sdc.Tasks.Tools.NUnit"/>.
    /// </para>
    /// <para><i>testTotal (Required)</i></para>
    /// <para>
    /// List of the total number of tests for each of the categories specified in <i>categories</i>. This will probably come from running the NUnit test <see cref="Microsoft.Sdc.Tasks.Tools.NUnit"/>.
    /// </para>
    /// <para><i>testTimeTaken (Required)</i></para>
    /// <para>
    /// List of the times taken for the tests for each of the categories specified in <i>categories</i>. This will probably come from running the NUnit test <see cref="Microsoft.Sdc.Tasks.Tools.NUnit"/>.
    /// </para>
    /// <para><i>outputXmlFilename</i></para>
    /// <para>The name of the xml filename to contain the test summary. Defaults to "testsummary.xml"
    /// </para>
    /// <para><i>xsltFilename</i></para>
    /// <para>The name of the Xsl stylesheet to use to transform the Xml summary into Html, if desrired.
    /// </para>
    /// <para><i>outputHtmlFilename</i></para>
    /// <para>The name of the file in which the transformed Xml summary is to be stored. Defaults to "Output\testsummary.html"
    /// </para>
    /// </remarks>
    /// <example>
    /// <code><![CDATA[
    /// <Project>
    ///     <Target Name="Test" >
    ///                 <CompileTestSummary Categories="Category1; Category2; Category3; Category4" TestPass="2; 1; 1; 2" TestFail="0; 0; 0; 1" TestNoRun="1; 0; 0; 0" TestTotal="3; 1; 1; 2" TestTimeTaken="12; 10; 5; 9" OutputXmlFilename="C:\Output\Output.xml" XsltFilename="C:\Output\Output.xsl" OutputHtmlFilename="C:\Output\MyResults.htm">
    ///                     <Output TaskParameter="TotalTestFailures" ItemName="TotalTestFailuresItem" />
    ///                 </CompileTestSummary>    
    ///     </Target>
    /// </Project>
    /// ]]></code>    
    /// </example>

    public class CompileTestSummary : TaskBase
    {
        private string[] categories;
        private string[] testPass;
        private string[] testFail;
        private string[] testNoRun;
        private string[] testTotal;
        private string[] testTimeTaken;
        private string outputXmlFilename;
        private string xsltFilename;
        private string outputHtmlFilename;
        private string[] totalTestFailures = { string.Empty };

        /*
         * Lists for holding the sorted categories and asscoiated
         * test results.
         */
        ArrayList testCategoryList = new ArrayList();
        ArrayList testPassList = new ArrayList();
        ArrayList testFailList = new ArrayList();
        ArrayList testNotRunList = new ArrayList();
        ArrayList testTimeList = new ArrayList();
        ArrayList testTotalList = new ArrayList();

        const string defaultCategory = @"{0F0A93AC-97D2-4a70-9993-290E3FC590FC}";

        /// <summary>
        /// Initializes a new instance of the CompileTestSummary class.
        /// </summary>
        public CompileTestSummary()
        {
        }

        /// <summary>
        /// Performs the action of this task.
        /// </summary>
        protected override void InternalExecute()
        {
            XmlDocument resultsXml = new XmlDocument();
            if (OutputXmlFilename.Length == 0)
            {
                // set the outputfile to the default.
                outputXmlFilename = @"testsummary.xml";
            }

            #region Execute code

            /*
                * Because the test results OutputItems just get appended to the OutputItem Array
                * we may have multiple entries in the input lists.
                * This needs to be sorted again so we have unique categories in the testCategoryList
                * and corresponding numbers.
                */

            AddCategoriesToList(categories);

            /*
                * Hold the totals for each category
                */
            int totalTests = 0;
            int failTotal = 0;
            int noRunTotal = 0;
            int passTotal = 0;
            double timeTotal = 0;

            for (int j = 0; j < categories.Length; j++)
            {
                int index = GetCategoryIndex(categories[j]);

                testPassList[index] = ((int) testPassList[index]) + Convert.ToInt32(testPass[j], CultureInfo.InvariantCulture);
                passTotal += Convert.ToInt32(testPass[j], CultureInfo.InvariantCulture);

                testFailList[index] = ((int) testFailList[index]) + Convert.ToInt32(testFail[j], CultureInfo.InvariantCulture);
                failTotal += Convert.ToInt32(testFail[j], CultureInfo.InvariantCulture);

                testTotalList[index] = ((int) testTotalList[index]) + Convert.ToInt32(testTotal[j], CultureInfo.InvariantCulture);
                totalTests += Convert.ToInt32(testTotal[j], CultureInfo.InvariantCulture);

                testNotRunList[index] = ((int) testNotRunList[index]) + Convert.ToInt32(testNoRun[j], CultureInfo.InvariantCulture);
                noRunTotal += Convert.ToInt32(testNoRun[j], CultureInfo.InvariantCulture);

                testTimeList[index] = ((double) testTimeList[index]) + Convert.ToDouble(testTimeTaken[j], CultureInfo.InvariantCulture);
                timeTotal += Convert.ToDouble(testTimeTaken[j], CultureInfo.InvariantCulture);
            }
            //set our outputitem.
            totalTestFailures[0] = failTotal.ToString(CultureInfo.InvariantCulture);
            /*
                * Build the xml file
                * <?xml version='1.0'?>
                * <?xml-stylesheet type="text/xsl" href="testsummary.xslt" ?>
                * writer.WriteRaw("\n<?xml-stylesheet type=\"text/xsl\" href=\"testsummary.xslt\" ?>");
                */

            XmlTextWriter writer = new XmlTextWriter(outputXmlFilename, System.Text.Encoding.Default);
            writer.WriteRaw(@"<?xml version='1.0'?>");
            writer.Formatting = Formatting.Indented;

            writer.WriteStartElement("testsummary");
            writer.WriteAttributeString("version", "1.0");
            writer.WriteAttributeString("Date", XmlConvert.ToString(DateTime.Now,"dd-MM-yyyy"));
            writer.WriteAttributeString("total", XmlConvert.ToString(totalTests));
            writer.WriteAttributeString("passed", XmlConvert.ToString(passTotal));
            writer.WriteAttributeString("failed", XmlConvert.ToString(failTotal));
            writer.WriteAttributeString("not-run", XmlConvert.ToString(noRunTotal));
            writer.WriteAttributeString("time", XmlConvert.ToString(timeTotal));
            writer.WriteStartElement("results");

            /*
                * Walk through our sorted list and write out to XML.
                */
            for (int i = 0; i < testCategoryList.Count; i++)
            {
                writer.WriteStartElement("category");
                if (testCategoryList[i].ToString() == defaultCategory)
                {
                    writer.WriteAttributeString("name", "Default");
                }
                else
                {
                    writer.WriteAttributeString("name", testCategoryList[i].ToString());
                }
                writer.WriteAttributeString("pass", testPassList[i].ToString());
                writer.WriteAttributeString("fail", testFailList[i].ToString());
                writer.WriteAttributeString("notrun", testNotRunList[i].ToString());
                writer.WriteAttributeString("total", testTotalList[i].ToString());
                writer.WriteAttributeString("time", testTimeList[i].ToString());
                writer.WriteEndElement();

                
                Log.LogMessageFromResources(MessageImportance.Low, "Test.Summary",
                    testCategoryList[i].ToString(),
                    testPassList[i].ToString(),
                    testFailList[i].ToString(),
                    testNotRunList[i].ToString(),
                    testTotalList[i].ToString(),
                    testTimeList[i].ToString());
            }

            /*
                * Close off the Xml elements
                */
            writer.WriteEndElement();
            writer.WriteEndElement();
            writer.Close();
            Log.LogMessageFromResources(MessageImportance.Low, "Test.ResultsLocation", outputXmlFilename);

            //If we have specified a transform file in the proj file then here is the place to apply it.
            if (XsltFilename.Length != 0)
            {
                //we have an xslt specified check it exists
                if (System.IO.File.Exists(xsltFilename))
                {
                    //make sure outputhtmlfilename is valid if not use default
                    if (OutputHtmlFilename.Length == 0)
                    {
                        //use default
                        outputHtmlFilename = @"Output\testsummary.html";
                    }

                    XslCompiledTransform sourceXslt = new XslCompiledTransform();
                    sourceXslt.Load(XsltFilename);
                    sourceXslt.Transform(outputXmlFilename, outputHtmlFilename); 

                    Log.LogMessageFromResources(MessageImportance.Low, "Test.XmlResultsTransformed", outputHtmlFilename);
                }
            }
            #endregion
        }

        /// <summary>
        /// Builds our master category list with no duplicates
        /// </summary>
        /// <param name="categories"></param>
        private void AddCategoriesToList(string[] categories)
        {
            bool found = false;
            string categoryTrimmed = null;

            foreach (string category in categories)
            {
                categoryTrimmed = category.Trim().ToLower(CultureInfo.InvariantCulture);
                if (categoryTrimmed.Length == 0)
                {
                    categoryTrimmed = defaultCategory.Trim().ToLower(CultureInfo.InvariantCulture);
                }

                found = false;
                foreach (string testcategory in testCategoryList)
                {
                    if (categoryTrimmed == testcategory.Trim().ToLower(CultureInfo.InvariantCulture))
                    {
                        found = true;
                        break;
                    }

                }
                if (!found)
                {
                    testCategoryList.Add(category.Trim());
                    testPassList.Add(0);
                    testFailList.Add(0);
                    testTotalList.Add(0);
                    testNotRunList.Add(0);
                    testTimeList.Add(0d);
                }
            }
            return;
        }

        /// <summary>
        /// detemines the location of a category in the main array list
        /// </summary>
        /// <param name="category"></param>
        /// <returns>index of the category</returns>
        private int GetCategoryIndex(string category)
        {
            int index = -1;
            category = category.Trim().ToLower(CultureInfo.InvariantCulture);
            if (category.Length == 0)
            {
                category = defaultCategory.Trim().ToLower(CultureInfo.InvariantCulture);
            }

            for (int i = 0; i < testCategoryList.Count; i++)
            {
                string testcategory = ((string) testCategoryList[i]).Trim().ToLower(CultureInfo.InvariantCulture);
                if (category == testcategory)
                {
                    index = i;
                    break;
                }
            }
            return index;
        }

        [Required]
        public string[] Categories
        {
            get
            {
                return categories;
            }
            set
            {
                categories = value;
            }
        }

        [Required]
        public string[] TestPass
        {
            get
            {
                return testPass;
            }
            set
            {
                testPass = value;
            }
        }

        [Required]
        public string[] TestFail
        {
            get
            {
                return testFail;
            }
            set
            {
                testFail = value;
            }
        }

        [Required]
        public string[] TestNoRun
        {
            get
            {
                return testNoRun;
            }
            set
            {
                testNoRun = value;
            }
        }

        [Required]
        public string[] TestTotal
        {
            get
            {
                return testTotal;
            }
            set
            {
                testTotal = value;
            }
        }

        [Required]
        public string[] TestTimeTaken
        {
            get
            {
                return testTimeTaken;
            }
            set
            {
                testTimeTaken = value;
            }
        }

        public string OutputXmlFilename
        {
            get
            {
                return (outputXmlFilename == null ? string.Empty : outputXmlFilename);
            }
            set
            {
                outputXmlFilename = value;
            }
        }

        public string XsltFilename
        {
            get
            {
                return (xsltFilename == null ? string.Empty : xsltFilename);
            }
            set
            {
                xsltFilename = value;
            }

        }

        public string OutputHtmlFilename
        {
            get
            {
                return (outputHtmlFilename == null ? string.Empty : outputHtmlFilename);
            }
            set
            {
                outputHtmlFilename = value;
            }

        }

        [Output]
        public string[] TotalTestFailures
        {
            get
            {
                return totalTestFailures;
            }

        }
    }
}